package com.xb.loan.fund.impl;

import com.xb.loan.cashier.service.constant.ICashierEnumConst;
import com.xb.loan.fund.dao.CashCheckResultDao;
import com.xb.loan.fund.dao.bean.CashCharge;
import com.xb.loan.fund.dao.bean.CashCheckResult;
import com.xb.loan.fund.dao.bean.CashEncash;
import com.xb.loan.fund.service.ICashChargeService;
import com.xb.loan.fund.service.ICashEncashService;
import com.xb.loan.fund.service.IFundService;
import com.xb.loan.fund.service.bean.CashChargeVO;
import com.xb.loan.fund.service.bean.CashCheckResultVO;
import com.xb.loan.fund.service.bean.CashEncashVO;
import com.xb.loan.fund.service.constant.FundConst;
import com.xb.loan.fund.service.exception.IFundServiceException;
import com.xb.loan.log.exception.BizException;
import com.xb.loan.util.SpringHelper;
import com.xb.loan.util.cache.redis.comm.FormatOperation;
import com.xb.loan.util.common.ObjectOperation;
import com.xb.loan.util.log.DefaultLoggerFacotry;
import com.xb.loan.util.log.LogFormatUtil;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Service("fundService")
public class FundServiceImpl implements IFundService {
	private static Logger logger = DefaultLoggerFacotry.getLogger(FundServiceImpl.class);
	
	@Value("${ftp.ip}")
	private String FTP_ip;
	@Value("${ftp.port}")
	private int FTP_port;
	@Value("${ftp.username}")
	private String FTP_username;
	@Value("${ftp.password}")
	private String FTP_password;
	private final static String TempLocalFile = "account_file_temp.";
	
	@Autowired
	private CashCheckResultDao cashCheckResultDao;
	
	@Autowired
	private ICashChargeService cashChargeService;
	
	@Autowired
	private ICashEncashService cashEncashService;
	
	@Override
	public void checkAccount(String address, String accountPeriod, int fundType) throws BizException {
		/*String tempAccountFileName = TempLocalFile + System.currentTimeMillis();
		FtpUtil util = new FtpUtil(FTP_username, FTP_password, FTP_ip, FTP_port);
		util.download(address, tempAccountFileName);
		//1、读取对账文件，并验证总行数是否匹配
		File tempAccountFile = new File(tempAccountFileName);
		String[] lines = verifyAccountFile(tempAccountFile);
		//2、如果对账文件中没有数据，直接返回。
		if(lines == null || lines.length == 0)
			return;
		//3、对账
		CashCheckResult cashCheckResult = new CashCheckResult();
		cashCheckResult.setCheckResultId(FormatOperation.assemblySeq(FundConst.PriKey_Name, FundConst.PriKey_Length));
		cashCheckResult.setAccountPeriod(accountPeriod);
		cashCheckResult.setAddress(address);
		if(fundType == FundConst.FundType_Charge)
			checkAccountCharge(accountPeriod, lines, cashCheckResult);
		else
			checkAccountEncash(accountPeriod, lines, cashCheckResult);
		//4、删除对账的临时文件
		tempAccountFile.delete();*/
	}

	//读取对账文件，并验证总行数是否匹配
	private String[] verifyAccountFile(File file) throws BizException {
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			int totalNum = Integer.parseInt(br.readLine());
			List<String> list = new LinkedList<String>();
			String line = null;
			while((line = br.readLine()) != null) {
				list.add(line);
			}
			br.close();
			if(list.size() != totalNum)
				throw IFundServiceException.AccountFileInComplete;
			return list.toArray(new String[0]);
		} catch (FileNotFoundException e) {
			logger.error(file.getAbsolutePath() +" does not exist!");
		} catch (NumberFormatException e) {
			logger.error(file.getAbsolutePath() +" format does not correct!");
		} catch (IOException e) {
			logger.error(file.getAbsolutePath() + e.getMessage());
		}
		return null;
	}

	//提现对账
	private void checkAccountEncash(String accountPeriod, String[] lines, CashCheckResult cashCheckResult) throws BizException {
		cashCheckResult.setFundType(FundConst.FundType_Encash);
		//1、清除此账期记录的对账结果（如果有）
		cashEncashService.clearAccountStatus(accountPeriod);
		//2、依次使用对账文件的数据进行对账
		boolean checkAccountResultOK = true;
		for(String line : lines){
			String[] items = line.split("\\|");
			String cashierOrderId = items[0];
			String encashId =  items[1];
			String userId = items[4];
			double money = Double.parseDouble(items[5]);
			int status = Integer.parseInt(items[6]) == ICashierEnumConst.CashierEncashStatus.SUCCESS.getStatus() ? FundConst.EncashStatus_Success : FundConst.EncashStatus_Fail;
			String payCardNo = items[9];
			
			CashEncashVO cashEncash = cashEncashService.queryCashEncashById(encashId);
			if(cashEncash == null){//少账
				checkAccountResultOK = false;
				cashCheckResult.addLessMoney(money);
				cashEncashService.updateAccountStatus(encashId, FundConst.CheckAccountStatus_Less);
				logger.error(LogFormatUtil.getFormatMsg(new String[]{"encashId", "cashierChargeId", "money", "desc"},
						new String[]{encashId, cashierOrderId, items[5], "miss encashOrder!"}));
				continue;
			}else{
				//相符
				if(cashEncash.getEncashId().equals(encashId) && cashEncash.getCashierOrderId().equals(cashierOrderId) 
						&& cashEncash.getUserId().equals(userId) && cashEncash.getMoney() == money 
						&& cashEncash.getCardNo().equals(payCardNo)){
					// 我方为失败，收银台为成功，错账
					if(cashEncash.getStatus() == FundConst.EncashStatus_Fail && status == FundConst.EncashStatus_Success){
						checkAccountResultOK = false;
						cashCheckResult.addErrorMoney(money);
						cashEncashService.updateAccountStatus(encashId, FundConst.CheckAccountStatus_Error);
						logger.warn(LogFormatUtil.getFormatMsg(new String[]{"encashId", "cashierChargeId", "money", "desc"},
								new String[]{encashId, cashierOrderId, items[5], "encashResult error!"}));
					}else{//先处理业务数据，再处理对账数据
						CashEncashServiceImpl impl = (CashEncashServiceImpl) cashEncashService;
						impl.encashResultInner(ObjectOperation.convertByClasses(cashEncash, CashEncash.class), status);
						cashCheckResult.addRightMoney(money);
						cashEncashService.updateAccountStatus(encashId, FundConst.CheckAccountStatus_OK);
					}
				}else{//错账
					checkAccountResultOK = false;
					cashCheckResult.addErrorMoney(money);
					cashEncashService.updateAccountStatus(encashId, FundConst.CheckAccountStatus_Error);
					logger.warn(LogFormatUtil.getFormatMsg(new String[]{"encashId", "cashierChargeId", "money", "desc"},
							new String[]{encashId, cashierOrderId, items[5], "encashResult error!"}));
				}
				continue;
			}
		}
		//3、检查本地数据是否有多账数据
		if (!checkAccountEncashMore(accountPeriod, FundConst.EncashStatus_Success, cashCheckResult))
			checkAccountResultOK = false;
		if (!checkAccountEncashMore(accountPeriod, FundConst.EncashStatus_Fail, cashCheckResult))
			checkAccountResultOK = false;
		//4、检查此次对账结果是否平账
		if(checkAccountResultOK)
			cashCheckResult.setStatus(FundConst.CheckAccountResult_Good);
		else
			cashCheckResult.setStatus(FundConst.CheckAccountResult_Bad);
		//5、保存对账结果，如果是重复对账，将之前的结果清除
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("accountPeriod", accountPeriod);
		params.put("fundType", FundConst.FundType_Encash);
		cashCheckResultDao.deleteByAccountPeriodAndType(params);
		cashCheckResultDao.insert(cashCheckResult);
		logger.info(LogFormatUtil.getFormatMsg(new String[]{"accountPeriod", "fundType", "desc"},
				new String[]{accountPeriod, String.valueOf(FundConst.FundType_Encash), "checkAccount end!"}));
	}
	
	private boolean checkAccountEncashMore(String accountPeriod, int status, CashCheckResult checkAccountResult) throws BizException{
		boolean checkAccountResultOK = true;
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("accountPeriod", accountPeriod);
		params.put("accountStatus", FundConst.CheckAccountStatus_Wait);
		params.put("status", status);
		List<CashEncashVO> successWaitList = cashEncashService.queryAllCashEncash(accountPeriod,
		        FundConst.CheckAccountStatus_Wait, status);
		
		if(successWaitList != null && successWaitList.size() > 0){//多账
			checkAccountResultOK = false;
			for(CashEncashVO cashEncash : successWaitList){
				cashEncashService.updateAccountStatus(cashEncash.getEncashId(), FundConst.CheckAccountStatus_More);
				checkAccountResult.addMoreMoney(cashEncash.getMoney());
				logger.warn(LogFormatUtil.getFormatMsg(new String[]{"encashId", "userId", "money", "desc"},
						new String[]{cashEncash.getEncashId(), cashEncash.getUserId(), String.valueOf(cashEncash.getMoney()), "more chargeRecord!"}));
				
			}
		}
		return checkAccountResultOK;
	}

	//充值对账
	private void checkAccountCharge(String accountPeriod, String[] lines, CashCheckResult cashCheckResult) throws NumberFormatException, BizException {
		logger.info(LogFormatUtil.getFormatTemplate(new String[]{"accountPeriod", "fundType", "desc"}),
				new String[]{accountPeriod, String.valueOf(FundConst.FundType_Charge), "checkAccount begin!"});
		cashCheckResult.setFundType(FundConst.FundType_Charge);
		//1、清除此账期记录的对账结果
		cashChargeService.clearAccountStatus(accountPeriod);
		//2、依次使用对账文件的数据进行对账
		boolean checkAccountResultOK = true;
		for(String line : lines){
			String[] items = line.split("\\|");
			String cashierOrderId = items[0];
			String chargeId =  items[1];
			double money = Double.parseDouble(items[5]);
			CashChargeVO cashChargeVO = cashChargeService.queryCashChargeById(chargeId);
			if(cashChargeVO == null){//少账
				checkAccountResultOK = false;
				cashCheckResult.addLessMoney(money);
				logger.error(LogFormatUtil.getFormatMsg(new String[]{"chargeId", "cashierChargeId", "money", "desc"},
						new String[]{chargeId, cashierOrderId, items[5], "miss chargeOrder!"}));
			}else{
				//相符
				if(cashChargeVO.getChargeId().equals(chargeId) && cashChargeVO.getCashierOrderId().equals(cashierOrderId) 
						&& cashChargeVO.getUserId().equals(items[4]) && cashChargeVO.getMoney() == money){
					if(cashChargeVO.getStatus() == FundConst.ChargeStatus_Wait){
						//我方为处理中，收银台为成功，为用户加钱
						CashChargeServiceImpl impl = (CashChargeServiceImpl)cashChargeService;
						impl.chargeResultInner(ObjectOperation.convertByClasses(cashChargeVO, CashCharge.class),
						        Integer.parseInt(items[7]), FundConst.ChargeStatus_Success, null, null, null, cashierOrderId);
					}
					cashCheckResult.addRightMoney(money);
					cashChargeService.updateAccountStatus(chargeId, FundConst.CheckAccountStatus_OK);
				}else{//错账
					checkAccountResultOK = false;
					cashCheckResult.addErrorMoney(money);
					cashChargeService.updateAccountStatus(chargeId, FundConst.CheckAccountStatus_Error);
					logger.warn(LogFormatUtil.getFormatMsg(new String[]{"chargeId", "cashierChargeId", "money", "desc"},
							new String[]{chargeId, cashierOrderId, items[5], "chargeResult error!"}));
				}
			}
		}
		//3、检查本地数据是否有多账数据
		List<CashChargeVO> list = cashChargeService.queryAllCashCharge(accountPeriod,
		        FundConst.CheckAccountStatus_Wait, FundConst.ChargeStatus_Success);
		
		if(list != null && list.size() > 0){//多账
			checkAccountResultOK = false;
			for(CashChargeVO cashCharge : list){
				cashChargeService.updateAccountStatus(cashCharge.getChargeId(), FundConst.CheckAccountStatus_More);
				cashCheckResult.addMoreMoney(cashCharge.getMoney());
				logger.warn(LogFormatUtil.getFormatMsg(new String[]{"chargeId", "userId", "money", "desc"},
						new String[]{cashCharge.getChargeId(), cashCharge.getUserId(), String.valueOf(cashCharge.getMoney()), "more chargeRecord!"}));
				
			}
		}
		//4、检查此次对账结果是否平账
		if(checkAccountResultOK)
			cashCheckResult.setStatus(FundConst.CheckAccountResult_Good);
		else
			cashCheckResult.setStatus(FundConst.CheckAccountResult_Bad);
		//5、保存对账结果，如果是重复对账，将之前的结果清除
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("accountPeriod", accountPeriod);
		params.put("fundType", FundConst.FundType_Charge);
		cashCheckResultDao.deleteByAccountPeriodAndType(params);
		cashCheckResultDao.insert(cashCheckResult);
		logger.info(LogFormatUtil.getFormatTemplate(new String[]{"accountPeriod", "fundType", "desc"}),
				new String[]{accountPeriod, String.valueOf(FundConst.FundType_Charge), "checkAccount end!"});
	}

	@Override
	public void reCheckAccount(String checkResultId) throws BizException {
		CashCheckResult checkAccountResult = cashCheckResultDao.queryCashCheckResultById(checkResultId);
		if(checkAccountResult == null){
			logger.warn(LogFormatUtil.getFormatTemplate(new String[]{"checkResultId", "desc"}),
				new String[]{checkResultId, "checkAccountRecord doesn't exist!"});
			throw IFundServiceException.CheckAccountRecordNotExist;
		}
		checkAccount(checkAccountResult.getAddress(), checkAccountResult.getAccountPeriod(), checkAccountResult.getFundType());
	}

	public static void main(String[] args) throws IOException, BizException {
//		FtpUtil util = new FtpUtil("admin", "admin", "127.0.0.1", 2121);
//		util.download("/charge_account.txt", "D:/aaa");
		SpringHelper.init("classpath*:/META-INF/applicationContext-core.xml");
		IFundService fundService = SpringHelper.getBean("fundService");
//		fundService.checkAccount("/charge_account.txt", "20150918", FundConst.FundType_Charge);
		fundService.checkAccount("/encash_account.txt", "20150918", FundConst.FundType_Encash);
	}

	@Override
	public List<CashCheckResultVO> queryCashCheckResult(String startDate, String endDate, int pageNum, int numPerPage) throws BizException {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("startDate", startDate);
		params.put("endDate", endDate);
		int start = (pageNum - 1) * numPerPage + 1;
		int end = pageNum * numPerPage;
		params.put("start", start);
		params.put("end", end);
		
		List<CashCheckResult> list = cashCheckResultDao.queryCashCheckResult(params);
		if(list == null || list.size() == 0)
			return null;
		List<CashCheckResultVO> result = ObjectOperation.convertByClassesList(list, CashCheckResultVO.class);
		return result;
	}

	@Override
	public int queryCashCheckResultCount(String startDate, String endDate) throws BizException {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("startDate", startDate);
		params.put("endDate", endDate);
		return cashCheckResultDao.queryCashCheckResultCount(params);
	}
	
	public boolean checkChargeEncashOver(String accountId) throws BizException{
	
		//验证用户是否有未完成的提现记录
	    if(cashEncashService.queryCashEncashByAccountId(accountId) > 0 ){
	        throw IFundServiceException.userCashEnCashError;
	    }
	    //验证用户是否有未完成的充值记录
	    if(cashChargeService.queryCashChargeTmpByAccountId(accountId) > 0){
	        throw IFundServiceException.userCashChargeTmpError;
	    }
	    return true;
	}
}
